import {Injectable, OnInit} from '@angular/core';
import {Observable, Subject} from 'rxjs';
import {filter, map, mergeMap, tap} from 'rxjs/operators';
import Feature from 'ol/Feature';
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import Stroke from 'ol/style/Stroke';
import Text from 'ol/style/Text';
import Point from 'ol/geom/Point';
import VectorSource from 'ol/source/Vector';
import VectorLayer from 'ol/layer/Vector';
import GeoJSON from 'ol/format/GeoJSON';
import {Extent} from 'ol/extent';
import {NzMessageService} from 'ng-zorro-antd';
import {OlFeature, OlLayer} from '../../../../map/base-map/entity/ol';
import {SysRegionTreeNodeService} from './sys-region-tree-node.service';
import {RegionLevel, SysRegion} from '../entity/sys-region';
import Geometry from 'ol/geom/Geometry';
import {transform} from 'ol/proj';
import {ColorLike} from 'ol/colorlike';
import {Color} from 'ol/color';
import CircleStyle from 'ol/style/Circle';
import GeometryType from 'ol/geom/GeometryType';
import Polygon from 'ol/geom/Polygon';
import {MultiPolygon} from 'ol/geom';
import MultiPoint from 'ol/geom/MultiPoint';

export type ShowDeepType = 1 | 2;
export interface RegionChangeObject {
    extent?: Extent;
    features?: OlFeature[];
    outerData?: SysRegion;
    subData?: SysRegion[];
}

@Injectable({
    providedIn: 'root'
})
export class SysRegionLayerService {

    public labels = {};
    public defaultFontColor: ColorLike | Color = [255, 255, 255, 1];
    public defaultFillColor: ColorLike | Color = [255, 255, 255, 0.5];
    public defaultStrokeColor: ColorLike | Color = [64, 64, 64, 0.75];
    private _showLevel: RegionLevel = 'COUNTRY';
    private _showDeep: ShowDeepType = 2;
    private getJsonFormat = new GeoJSON();
    private _highLightColor: ColorLike | Color = [255, 175, 0, 0.5];
    private _highLightFontColor: ColorLike | Color = [255, 255, 255, 1];
    private dynamic = true;
    private _colors = {};
    private _colors2 = {};
    private _colorsIndex = 0;

    constructor(private sysRegionService: SysRegionTreeNodeService,
                private nzMessageService: NzMessageService
    ) {
        this._layer = new VectorLayer({
            source: new VectorSource(),
            style: (feature: Feature, resolution: number) => {
                const highLight = feature.get('highLight');

                // 如果是高亮
                if (highLight) {
                    return this.getRegionStyle(feature, resolution, true);
                }

                if (this._showDeep === 1) {
                    if (feature.get('level') === this._showLevel) {
                        return this.getRegionStyle(feature, resolution);
                    } else if (feature.get('level') === SysRegionLayerService.getNextRegionLevel(this._showLevel)) {
                        return this.getRegionStyle(feature, resolution);
                    }
                } else if (this._showDeep === 2) {
                    const level = feature.get('level');
                    if (level === SysRegionLayerService.getNextRegionLevel(this._showLevel)) {
                        return this.getRegionStyle(feature, resolution);
                    } else if (level === SysRegionLayerService.getNextRegionLevel(SysRegionLayerService.getNextRegionLevel(this._showLevel))) {
                        return new Style({
                            fill: new Fill({
                                color: this.getColor(feature),
                            }),
                            stroke: new Stroke({
                                color: [255, 255, 255, 1],
                                width: 0.1
                            }),
                            zIndex: 2
                        });
                    }
                }
                return null;
            }
        });
    }

    private _sourceChange: Subject<RegionChangeObject> = new Subject<RegionChangeObject>();

    get sourceChange(): Observable<RegionChangeObject> {
        return this._sourceChange;
    }

    private _layer: VectorLayer;

    get layer(): OlLayer {
        return this._layer;
    }

    private static getNextRegionLevel(level: RegionLevel | string): RegionLevel | null {
        if (level === 'COUNTRY') {
            return 'PROVINCE';
        } else if (level === 'PROVINCE') {
            return 'CITY';
        } else if (level === 'CITY') {
            return 'DISTRICT';
        } else if (level === 'DISTRICT') {
            return 'STREET';
        } else if (level === 'STREET') {
            return 'CUN';
        }
        return null;
    }

    setColors(color1Obj, color2Obj?) {
        this._colors = color1Obj || {};
        this._colors2 = color2Obj || {};
        this.dynamic = false;
    }

    setUseDefaultColor() {
        this._colors = {};
        this._colors2 = {};
        this.dynamic = true;
    }

    reset() {
        this.setUseDefaultColor();
        this._highLightColor = [255, 175, 0, 0.5];
        this._highLightFontColor = [255, 255, 255, 1];
        this.labels = {};
        this.defaultFontColor = [255, 255, 255, 1];
        this.defaultFillColor = [255, 255, 255, 0.5];
        this.defaultStrokeColor = [64, 64, 64, 0.75];
    }

    setByParentCode(parentCode: string, deep: ShowDeepType = 1) {
        this.sysRegionService.get(parentCode).pipe(
            // 只有returnForm为true才通过
            map(returnForm => {
                if (returnForm && returnForm.success) {
                    return returnForm.message;
                } else {
                    this.nzMessageService.error('无法获取parentCode=' + parentCode + '的子节点！');
                    return [] as SysRegion[];
                }
            }),

            // 合并结果
            mergeMap((data: SysRegion) => {
                return this.sysRegionService.getChildren(parentCode, deep).pipe(
                    filter(returnForm => returnForm.success),
                    map(returnForm => [data, returnForm.message])
                );
            }),

            // 空操作，对流不进行更改，进行清除操作
            tap(() => {
                this._layer.getSource().clear();
            })
        ).subscribe((result: any) => {
            const [data, datas] = result;

            if (data) {
                this._showLevel = data.level;
            } else {
                this._showLevel = 'COUNTRY';
            }

            this._showDeep = deep;
            const features: Feature[] = [];

            datas.forEach((value: SysRegion) => {
                features.push(this.regionToFeature(value));
            });
            if (features.length > 0 && features[0].getGeometry().getType() === GeometryType.POINT) {
                const parentFeature = this.regionToFeature(data);
                parentFeature.set('areaName', '');
                features.push(parentFeature);
            }
            this._layer.getSource().addFeatures(features);
            this._sourceChange.next({
                extent: this._layer.getSource().getExtent(),
                features: features,
                outerData: data,
                subData: datas
            });
        });
    }

    /**
     * 根据feature 获取相应的 hover Feature
     * @param feature 当前hover Feature
     */
    getRealHoverFeature(feature: Feature): Feature | undefined {
        if (this._showDeep === 2) {
            if (feature.get('level') === SysRegionLayerService.getNextRegionLevel(this._showLevel)) {
                return feature;
            } else if (feature.get('level') === SysRegionLayerService.getNextRegionLevel(SysRegionLayerService.getNextRegionLevel(this._showLevel))) {
                return this._layer.getSource().getFeatureById(feature.get('parentCode'));
            }
        } else {
            if (feature.get('level') !== this._showLevel) {
                return feature;
            }
        }
        return;
    }

    clickFeature(feature: Feature) {
        this.setByParentCode(feature.get('code'), 1);
    }

    private getColor(feature: Feature): ColorLike | Color {
        const code = feature.get('code');
        const parentCode = feature.get('parentCode');
        const level = feature.get('level');
        if (this.dynamic === true) {
            if (level === SysRegionLayerService.getNextRegionLevel(this._showLevel)) {
                if (!this._colors[code]) {
                    this._colorsIndex++;
                    if (this._colorsIndex % 4 === 0) {
                        const gb = Math.random() * 155 + 50;
                        this._colors[code] = [gb, gb, 0, 1];
                    } else if (this._colorsIndex % 4 === 1) {
                        const gb = Math.random() * 155 + 50;
                        this._colors[code] = [0, gb, 0, 1];
                    } else if (this._colorsIndex % 4 === 2) {
                        const gb = Math.random() * 155 + 50;
                        this._colors[code] = [0, 0, gb, 1];
                    } else if (this._colorsIndex % 4 === 3) {
                        const gb = Math.random() * 155 + 50;
                        this._colors[code] = [255, 0, gb, 1];
                    }
                }
                return this._colors[code];
            } else {
                if (!this._colors2[code]) {
                    this._colors2[code] = [255, 255, 255, Math.random()];
                }
                return this._colors2[code];
            }
        } else {
            if (level === SysRegionLayerService.getNextRegionLevel(this._showLevel)) {
                if (!this._colors[code]) {
                    this._colors[code] = this.defaultFillColor;
                }
                return this._colors[code];
            } else {
                if (!this._colors2[code]) {
                    this._colors2[code] = this.defaultFillColor;
                }
                return this._colors2[code];
            }
        }
    }

    private getRegionStyle(feature: Feature, resolution: number, highlight?: boolean) {

        const fillColor = highlight ? this._highLightColor : this.getColor(feature);
        const fontColor = highlight ? this._highLightFontColor : this.defaultFontColor;
        const zIndex = highlight ? 4 : 3;

        const code = feature.get('code');
        const styles = [new Style({
            fill: new Fill({
                color: fillColor
            }),
            stroke: new Stroke({
                color: this.defaultStrokeColor,
                width: 0.5
            }),
            image: new CircleStyle({
                radius: 5,
                fill: new Fill({
                    color: '#ffa229'
                })
            }),
            zIndex: zIndex
        })];

        let name = feature.get('shortName') || feature.get('areaName');
        name = name.replace(/(省$)|(市$)|(自治区$)|(区$)|(县$)|(街道$)|(镇$)|(乡$)/, '');
        let geomType = null;
        if (feature.getGeometry()) {
            geomType = feature.getGeometry().getType();
            let offset = 0;
            if (geomType === GeometryType.POINT || geomType === GeometryType.MULTI_POINT) {
                offset = -15;
            }

            if (!feature.get('textGeom')) {
                let popupPoint = null;
                const geom = feature.getGeometry();
                const type = geom.getType();
                if (type === GeometryType.POLYGON) {
                    popupPoint = (geom as Polygon).getInteriorPoint();
                } else if (type === GeometryType.MULTI_POLYGON) {
                    const polygons = (geom as MultiPolygon).getPolygons();
                    let maxAreaPolygonIndex = 0;
                    let maxArea = 0;
                    for (let i = 0; i < polygons.length; i++) {
                        const area = polygons[i].getArea();
                        if (area > maxArea) {
                            maxAreaPolygonIndex = i;
                            maxArea = area;
                        }
                    }
                    popupPoint = polygons[maxAreaPolygonIndex].getInteriorPoint();
                } else if (type === GeometryType.POINT) {
                    popupPoint = geom;
                } else if (type === GeometryType.MULTI_POINT) {
                    popupPoint = (geom as MultiPoint).getPoint(0);
                }
                feature.set('textGeom', popupPoint);
            }

            const textGeometry = feature.get('textGeom');
            styles.push(new Style({
                geometry: textGeometry,
                text: new Text({
                    text: name,
                    fill: new Fill({
                        color: fontColor
                    }),
                    stroke: new Stroke({
                        color: fontColor,
                        width: 0.5
                    }),
                    font: '14px 微软雅黑',
                    offsetY: offset
                }),
                zIndex: zIndex
            }));
            if (this.labels[code]) {
                styles.push(new Style({
                    geometry: textGeometry,
                    text: new Text({
                        text: this.labels[code] + '',
                        fill: new Fill({
                            color: fontColor
                        }),
                        stroke: new Stroke({
                            color: fontColor,
                            width: 0.5
                        }),
                        font: '14px 微软雅黑',
                        offsetY: offset - 15
                    }),
                    zIndex: zIndex
                }));
            }
        }

        return styles;
    }

    private regionToFeature(value: SysRegion): Feature {
        const geoJson = value.geoJson || value.geoJsonForSub;
        let geom: Geometry = null;
        if (geoJson) {
            geom = this.getJsonFormat.readGeometry(geoJson);
            geom.transform('EPSG:4326', 'EPSG:3857');
        } else {
            geom = new Point(transform(JSON.parse(value.center), 'EPSG:4326', 'EPSG:3857'));
        }
        const feature = new Feature({
            geometry: geom,
            areaName: value.areaName,
            center: transform(JSON.parse(value.center), 'EPSG:4326', 'EPSG:3857'),
            pname: value.pname,
            code: value.code,
            parentCode: value.parentCode,
            level: value.level
        });
        feature.setId(value.code);
        return feature;
    }
}
